using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace BardsTaleViewer {
  public class Palette {
    private int _size;
    private LookupTypeEnum _lookupType;
    private int [] _colorsR;
    private int [] _colorsG;
    private int [] _colorsB;
    private uint [] _rawARGB;
    private int [] _rawRGB;

    private Color [] _colors;
    private Brush [] _brushes;
    private Pen [] _pens;

    private Bitmap _bitmap;
    //                                      0    36    73   109   146   182   219   255
    //                                      0    32    64    96   128   160   192   224
    //                                      0     1     2     3     4     5     6     7
    private static byte [] _lookUpST = { 0x00, 0x24, 0x49, 0x6D, 0x92, 0xB6, 0xDB, 0xFF };
    private static byte [] _lookUpAmigaToST = { 0x00, 0x00, 0x24, 0x00, 0x49, 0x00, 0x6D, 0x00, 0x92, 0x00, 0xB6, 0x00, 0xDB, 0x00, 0xFF, 0x00 };
    private static byte [] _lookUpSTe = {
  0x00, /* 0000 */
  0x22, /* 0001 */
  0x43, /* 0010 */
  0x66, /* 0011 */
  0x88, /* 0100 */
  0xAA, /* 0101 */
  0xCC, /* 0110 */
  0xEE, /* 0111 */
  0x11, /* 1000 */
  0x33, /* 1001 */
  0x44, /* 1010 */
  0x55, /* 1011 */
  0x99, /* 1100 */
  0xBB, /* 1101 */
  0xDD, /* 1110 */
  0xFF, /* 1111 */
};

    public enum LookupTypeEnum {
      ST,
      STe,
      Amiga,
      AmigaToST,
      None
    }

    public Palette (int [] rawRGB, LookupTypeEnum lookupType) {
      Init (rawRGB, lookupType);
    }

    public Palette (byte [] bytes, int address, int size, LookupTypeEnum lookupType) {
      int [] rawRGB = new int [size];
      for (int i = 0; i < size; i++) {
        int rgb = bytes.ReadWord (address + 2 * i);
        rawRGB [i] = rgb;
      }
      Init (rawRGB, lookupType);
    }

    private void Init (int [] rawRGB, LookupTypeEnum lookupType) {
      _lookupType = lookupType;

      byte [] lookup = null;
      if (lookupType == LookupTypeEnum.ST) {
        lookup = _lookUpST;
      }
      else if (lookupType == LookupTypeEnum.STe) {
        lookup = _lookUpSTe;
      }
      else if (lookupType == LookupTypeEnum.AmigaToST) {
        lookup = _lookUpAmigaToST;
      }

      _rawRGB = rawRGB;
      _size = rawRGB.Length;

      _colorsR = new int [_size];
      _colorsG = new int [_size];
      _colorsB = new int [_size];
      _rawARGB = new uint [_size];
      _colors = new Color [_size];
      _pens = new Pen [_size];
      _brushes = new SolidBrush [_size];

      for (int i = 0; i < _size; i++) {
        int r, g, b;
        //if (version == VersionEnum.AmigaCD32) {
        //  r = dump.ReadByte (paletteAddress++);
        //  v = dump.ReadByte (paletteAddress++);
        //  b = dump.ReadByte (paletteAddress++);
        //}

        int rgb = _rawRGB [i];
        //if ((rgb & 0xF000) != 0) {
        //  rgb -= 0xF888;
        //}
        //_rawRGB [i] = rgb;
        if (lookupType == LookupTypeEnum.ST) {
          //if ((rgb >> (8 + 3)) != 0)
          //  throw new Exception ();
          //if ((rgb >> (4 + 3)) != 0)
          //  throw new Exception ();
          //if ((rgb >> (0 + 3)) != 0)
          //  throw new Exception ();
          r = (rgb >> 8) & 0x0007;
          g = (rgb >> 4) & 0x0007;
          b = (rgb >> 0) & 0x0007;
        }
        else {
          r = (rgb >> 8) & 0x000F;
          g = (rgb >> 4) & 0x000F;
          b = (rgb >> 0) & 0x000F;
        }

        //r = (r << 5) + 16;
        //v = (v << 5) + 16;
        //b = (b << 5) + 16;
        if (lookupType != LookupTypeEnum.None) {
          if (lookupType == LookupTypeEnum.Amiga) {
            r *= 17;
            g *= 17;
            b *= 17;
          }
          else {
            r = lookup [r];
            g = lookup [g];
            b = lookup [b];
          }
        }
        _colorsR [i] = r;
        _colorsG [i] = g;
        _colorsB [i] = b;

        //_rawBGRA [i] = (uint) ((b << 24) | (v << 16) | (r << 8));
        _rawARGB [i] = (uint) ((0xFF << 24) | (r << 16) | (g << 8) | (b << 0));
        //if ((_colors1 [i] == 91) || (_colors2 [i] == 91) | (_colors3 [i] == 91)) {
        //  Console.WriteLine ("{0} {1} {2}", _colors1 [i], _colors2 [i], _colors3 [i]);
        //  int z = 0;
        //}
        //if (i < 32) {
        //  _colors1 [i] = (_colorsPatch [i] >> 16) & 0xFF;
        //  _colors2 [i] = (_colorsPatch [i] >> 8) & 0xFF;
        //  _colors3 [i] = (_colorsPatch [i] >> 0) & 0xFF;
        //}
        _colors [i] = Color.FromArgb (_colorsR [i], _colorsG [i], _colorsB [i]);
        _brushes [i] = new SolidBrush (_colors [i]);
        _pens [i] = new Pen (_colors [i]);
      }

      Size cellSize = new Size (16, 7);
      _bitmap = new Bitmap (16 * (cellSize.Width + 1) + 1, ((_size - 1) / 16 + 1) * (cellSize.Height + 1) + 1, PixelFormat.Format32bppPArgb);
      Graphics graphics = Graphics.FromImage (_bitmap);
      graphics.Clear (Color.Black);
      Font font = new Font (FontFamily.GenericSansSerif, 6.5f);
      for (int y = 0; y < 16; y++) {
        for (int x = 0; x < 16; x++) {
          if ((y * 16 + x) >= _size) {
            y = 16;
            break;
          }
          int index = y * 16 + x;
          Point xy = new Point (x * (cellSize.Width + 1) + 1, y * (cellSize.Height + 1) + 1);
          graphics.FillRectangle (_brushes [y * 16 + x], xy.X, xy.Y, cellSize.Width, cellSize.Height);
          //graphics.DrawStringOutlinedCentered (index.ToString (), font, Brushes.White, Brushes.Black, xy.X + cellSize.Width / 2, xy.Y + (cellSize.Height + 1) / 2);
        }
      }
    }

    public static Color GetColor (int rgb, LookupTypeEnum paletteLookup) {
      byte [] lookup = null;
      if (paletteLookup == LookupTypeEnum.ST) {
        lookup = _lookUpST;
      }
      else if (paletteLookup == LookupTypeEnum.AmigaToST) {
        lookup = _lookUpAmigaToST;
      }
      int r, g, b;
      r = (rgb >> 8) & 0x000F;
      g = (rgb >> 4) & 0x000F;
      b = (rgb >> 0) & 0x000F;

      //r = (r << 5) + 16;
      //v = (v << 5) + 16;
      //b = (b << 5) + 16;
      if (lookup != null) {
        r = lookup [r];
        g = lookup [g];
        b = lookup [b];
      }

      return (Color.FromArgb (r, g, b));
    }

    public Bitmap Bitmap {
      get {
        return (_bitmap);
      }
    }

    public int Size {
      get {
        return (_size);
      }
    }

    public Color GetColor (int i) {
      if ((i >= 0) && (i < _colors.Length)) {
        return (_colors [i]);
      }
      else {
        return (Color.Red);
      }
    }

    public uint GetRawARGB (int i) {
      if ((i >= 0) && (i < _rawARGB.Length)) {
        return (_rawARGB [i]);
      }
      else {
        return (0xFFFF0000);
      }
    }

    public Brush GetBrush (int i) {
      if ((i >= 0) && (i < _brushes.Length)) {
        return (_brushes [i]);
      }
      else {
        return (Brushes.Red);
      }
    }

    public Pen GetPen (int i) {
      if ((i >= 0) && (i < _pens.Length)) {
        return (_pens [i]);
      }
      else {
        return (Pens.Red);
      }
    }

    public int [] RawRGB {
      get {
        return (_rawRGB);
      }
    }

    public static Palette DefaultST {
      get {
        byte [] bytes = new byte [] {0x07, 0x77, 0x07, 0x00, 0x00, 0x70, 0x07, 0x70, 0x00, 0x07, 0x07, 0x07, 0x00, 0x77, 0x05, 0x55,
                                     0x03, 0x33, 0x07, 0x33, 0x03, 0x73, 0x07, 0x73, 0x03, 0x37, 0x07, 0x37, 0x03, 0x77, 0x00, 0x00};
        return (new Palette (bytes, 0, 16, LookupTypeEnum.ST));
      }
    }

    public static Palette Rearrange (Palette palette, int [] arrange) {
      int [] srcRawRGB = palette.RawRGB;
      int [] dstRawRGB = new int [srcRawRGB.Length];

      if (srcRawRGB.Length != arrange.Length)
        throw new Exception ();

      for (int i = 0; i < arrange.Length; i++)
        dstRawRGB [i] = srcRawRGB [arrange [i]];

      return (new Palette (dstRawRGB, palette._lookupType));
    }

    public LookupTypeEnum LookupType {
      get {
        return (_lookupType);
      }
    }
  }
}
